package com.ruoyi.wemedia.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.dtos.ResponseResult;
import com.ruoyi.common.enums.AppHttpCodeEnum;
import com.ruoyi.common.mybatis.core.page.PageQuery;
import com.ruoyi.common.mybatis.core.page.TableDataInfo;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.common.satoken.utils.LoginHelper;
import com.ruoyi.utils.constants.WemediaConstants;
import com.ruoyi.utils.exception.CustomException;
import com.ruoyi.wemedia.domain.Material;
import com.ruoyi.wemedia.domain.NewsMaterial;
import com.ruoyi.wemedia.domain.bo.MaterialBo;
import com.ruoyi.wemedia.kafka.producer.NewsStreamProducer;
import com.ruoyi.wemedia.mapper.MaterialMapper;
import com.ruoyi.wemedia.mapper.NewsMaterialMapper;
import com.ruoyi.wemedia.service.WmNewsAutoScanService;
import com.ruoyi.wemedia.service.WmNewsTaskService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.wemedia.domain.bo.NewsBo;
import com.ruoyi.wemedia.domain.vo.NewsVo;
import com.ruoyi.wemedia.domain.News;
import com.ruoyi.wemedia.mapper.NewsMapper;
import com.ruoyi.wemedia.service.INewsService;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 文章列表Service业务层处理
 *
 * @author xiaoqiang
 * @date 2022-10-15
 */
@RequiredArgsConstructor
@Service
@Slf4j
public class NewsServiceImpl implements INewsService {

    private final NewsMapper baseMapper;

    private  final MaterialMapper materialMapper;

    private final NewsMaterialMapper newsMaterialMapper;

    private final WmNewsAutoScanService newsAutoScanService;


    private final  WmNewsTaskService wmNewsTaskService;

    /**
     * 查询文章列表
     */
    @Override
    public NewsVo queryById(Long id){
        return baseMapper.selectVoById(id);
    }

    /**
     * 查询文章列表列表
     */
    @Override
    public TableDataInfo<NewsVo> queryPageList(NewsBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<News> lqw = buildQueryWrapper(bo);
        Page<NewsVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询文章列表列表
     */
    @Override
    public List<NewsVo> queryList(NewsBo bo) {
        LambdaQueryWrapper<News> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<News> buildQueryWrapper(NewsBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<News> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getUserId() != null, News::getUserId, bo.getUserId());
        lqw.eq(StringUtils.isNotBlank(bo.getTitle()), News::getTitle, bo.getTitle());
        lqw.eq(StringUtils.isNotBlank(bo.getContent()), News::getContent, bo.getContent());
        lqw.eq(bo.getType() != null, News::getType, bo.getType());
        lqw.eq(bo.getChannelId() != null, News::getChannelId, bo.getChannelId());
        lqw.eq(StringUtils.isNotBlank(bo.getLabels()), News::getLabels, bo.getLabels());
        lqw.eq(bo.getSubmitedTime() != null, News::getSubmitedTime, bo.getSubmitedTime());
        lqw.eq(StringUtils.isNotBlank(bo.getStatus()), News::getStatus, bo.getStatus());
        lqw.eq(bo.getPublishTime() != null, News::getPublishTime, bo.getPublishTime());
        lqw.eq(StringUtils.isNotBlank(bo.getReason()), News::getReason, bo.getReason());
        lqw.eq(StringUtils.isNotBlank(bo.getArticleId()), News::getArticleId, bo.getArticleId());
        lqw.eq(StringUtils.isNotBlank(bo.getImages()), News::getImages, bo.getImages());
        lqw.eq(bo.getEnable()!= null, News::getEnable, bo.getEnable());
        lqw.between(bo.getBeginPubDate() != null && bo.getEndPubDate() != null,
            News::getPublishTime,bo.getBeginPubDate(),bo.getEndPubDate());
        lqw.orderByDesc(News::getId);
        return lqw;
    }

    /**
     * 新增文章列表
     */
    @Override
    public R<T> insertByBo(NewsBo bo) {

        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss");
        log.info("-----------添加文章--------------" + simpleDateFormat.format(bo.getPublishTime()));
        News news = BeanUtil.toBean(bo, News.class);
        news.setPublishTime(bo.getPublishTime());

        //判断是否存在文章id
        //todo 如果封面是自动，那么先把封面的类型设置为null
        if(bo.getType()== WemediaConstants.WM_NEWS_AUTO_IMAGE){ //代表封面为自动  bug 判断short 记得类型转换一下
            news.setType(null);
        }
        //todo 保存或者修改文章数据到自媒体文章表中 （补全后续参数）  (userId createTime)
        news.setUserId(Long.valueOf(LoginHelper.getUserId()));
        news.setEnable((short)1);
        if(news.getId() == null){
            //todo 新增
            if(baseMapper.insert(news) < 1){
                return R.fail("文章添加时遇到错误！");
            }
        }else{
            //todo 修改
            //todo 3.1 补全文章的数据，然后新增到数据库即可 （判断是否为修改，如果为修改，删除所有素材）
            LambdaUpdateWrapper<NewsMaterial> delWrapper = new LambdaUpdateWrapper<>();
            delWrapper.eq(NewsMaterial::getNewsId,news.getId());
            newsMaterialMapper.delete(delWrapper);
            if(baseMapper.updateById(news) < 1){
                return R.fail("文章更新时遇到错误！");
            }
        }
        //todo 3.2 需要从文章中提取文章中的素材数据 （保存关联关系）
        List<String> materials = contentGetImages(news.getContent());
        //todo 3.3 需要从封面中提取素材 （保存关联关系）
        //todo 3.3.1 根据素材的内容，来获得所有的素材的ID
        saveWmNewsMaterialInfo(materials,news.getId(),WemediaConstants.WM_CONTENT_REFERENCE);
        //todo 3.4 如果文章的封面是自动  那么需要判断文章内容素材的个数
        //todo 思考：1.如果封面不是null需要做什么？把封面素材的关系保存到中间表    2.如果封面是null?根据内容来判断封面情况
        //收集封面图片
        List<String> images = Arrays.asList(bo.getImages().split(","));
        if(news.getType() == null){
            //todo 代表用户选择的是自动
            if(materials.size() == 0 || materials == null){
                //todo 3.4.1 如果图片 < 1 设置封面为无图
                news.setType(WemediaConstants.WM_NEWS_NONE_IMAGE);
            }else if(materials.size() > 0 && materials.size() < 3){
                //todo 3.4.2 如果图片 >= && < 3 设置为单图
                news.setType(WemediaConstants.WM_NEWS_SINGLE_IMAGE);
                //需要取出素材中的图片
                images = materials.stream().limit(1).collect(Collectors.toList());
            }else{
                //todo 3.4.3 如果图片 >= 3 设置为三图
                news.setType(WemediaConstants.WM_NEWS_MANY_IMAGE);
                images = materials.stream().limit(3).collect(Collectors.toList());
            }
            //todo 4.更新文章状态即可
            if(materials.size() > 0){
                String imageStr = org.apache.commons.lang3.StringUtils.join(images, ",");
                news.setImages(imageStr); //封面图片是不是久有了
            }
            if(baseMapper.updateById(news) < 1){
                return R.fail("更新素材关联时遇到错误！");
            }
        }
        //todo 5.保存封面和图片的关系
        saveWmNewsMaterialInfo(images,news.getId(),WemediaConstants.WM_COVER_REFERENCE);

        //提交审核
        try{
            //newsAutoScanService.autoScanWmNews(news.getId());
            wmNewsTaskService.addNewsToTask(news.getId(),news.getPublishTime());
        }catch (ServiceException ex){
            return R.fail("文章服务连接出现异常！");
            //ex.printStackTrace();
        }

        return R.ok();
    }

//    public static void main(String[] args) {
////        String s = "1";
////        List<String> images = Arrays.asList(s.split(","));
////        for (String l : images){
////            System.out.println(l);
////        }
//        System.out.println(AppHttpCodeEnum.IMG_MISS.getErrorMessage());
//    }

    /**
     * 修改文章列表
     */
    @Override
    public Boolean updateByBo(NewsBo bo) {
        News update = BeanUtil.toBean(bo, News.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(News entity){
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 批量删除文章列表
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if(isValid){
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        Long id = ids.iterator().next();
        LambdaUpdateWrapper<NewsMaterial> delWrapper = new LambdaUpdateWrapper<>();
        News news = new News();
        news.setId(id);
        delWrapper.eq(NewsMaterial::getNewsId,news.getId());
        newsMaterialMapper.delete(delWrapper);
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    /**
     * todo 保存素材和文章关系到中间表中
     * @param materials  需要关联的素材的集合
     * @param wmNewId  文章ID
     * @param type  素材的类型
     */
    private void saveWmNewsMaterialInfo(List<String> materials,Long wmNewId,Short type){
        if(materials!= null && materials.size()>0){
            LambdaQueryWrapper<Material> materialWrapper = new LambdaQueryWrapper<>();
            materialWrapper.in(Material::getUrl,materials);
            List<Material> wmMaterials = materialMapper.selectList(materialWrapper);
            if(wmMaterials == null || wmMaterials.size() == 0){
                //throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST);
                throw new ServiceException(AppHttpCodeEnum.DATA_NOT_EXIST.getErrorMessage());
            }
            //todo 判断素材和数据库的素材数量是否一致，如果不一致代表素材丢失了。
            if(wmMaterials.size() != materials.size()){
                throw new ServiceException(AppHttpCodeEnum.IMG_MISS.getErrorMessage());
                //throw new CustomException(AppHttpCodeEnum.IMG_MISS);
            }
            //todo 3.3.2 获得里面的所有的ID， 不需要所有的素材
            List<Long> wmMaterialIds = wmMaterials.stream().map(c -> c.getId()).collect(Collectors.toList());
            //todo 3.3.3 批量插入到数据库即可
            newsMaterialMapper.saveRelations(wmMaterialIds,wmNewId,type);
        }
    }


    /**
     * 从文章内容中抽取所有的素材图片
     * @return 内容中的所有图片
     */
    private List<String> contentGetImages(String content){
        //[{type:'images',value:'http://adsasdasd.jpg'},{type:'text',"value":"你好我好他也好！！！"}]
        List<String> materials = new ArrayList<>();
        /**
         * 1.你有什么？
         * 2.你要什么？
         * 3.把一个字符串转换成一个集合
         */
        List<Map> maps = JSON.parseArray(content, Map.class);
        /*for (Map map : maps) {
            if(map.get("type").equals("image")){
                materials.add((String)map.get("value"));
            }
        }*/
        materials = maps.stream().filter(c->c.get("type").equals("image"))
            .map(c->(String)c.get("value")).distinct().collect(Collectors.toList());
        return materials;
    }

    private final NewsStreamProducer newsStreamProducer;

    /**
     * 文章的上下架

     */
    @Override
    public R<T> downOrUp(NewsBo bo) {
        //1.检查参数
        if(bo.getId() == null){
            return R.fail("无效的参数！");
        }

        //2.查询文章
        News wmNews = baseMapper.selectById(bo.getId());//getById(dto.getId());
        if(wmNews == null){
            return R.fail("文章不存在！");
        }

        //3.判断文章是否已发布
        if(!wmNews.getStatus().equals(News.Status.PUBLISHED.getCode())){
            return R.fail("当前文章不是发布状态，不能上下架！");
        }

        //4.修改文章enable
        if(bo.getEnable() != null && bo.getEnable() > -1 && bo.getEnable() < 2){
            wmNews.setEnable(bo.getEnable());
            this.baseMapper.updateById(wmNews);
            //kafka
            newsStreamProducer.upordown(wmNews);
        }
        return R.ok();
    }
}
